/*
 * Semaphore.cpp
 *
 *  Created on: Oct 28, 2013
 *      Author: m
 */

#include "NewSemaphore.h"

namespace ipc
{

Semaphore::Semaphore( int id, int semAmmount ) :
        m_id( id ),
        m_semAmmount( semAmmount )
{
}

Semaphore::~Semaphore()
{
}

Semaphore& Semaphore::initialize( int initialValue )
{
    std::string error;
    semnum init;

    init.val = initialValue;
    init.array = new ushort[m_semAmmount];

    for ( int i = 0; i < m_semAmmount; i++ )
    {
        init.array[i] = initialValue;
    }

    if ( semctl( m_id, 0, SETALL, init ) < 0 )
    {
        error = strerror( errno );
        throw Exception( "Error while initializing the semaphore: " + error );
    }

    delete[] init.array;

    return *this;
}

void Semaphore::p( int index )
{
    std::string error;
    struct sembuf operation;

    operation.sem_num = index; // numero de semaforo
    operation.sem_op = -1; // restar 1 al semaforo
    operation.sem_flg = 0;

    if ( semop( m_id, &operation, 1 ) < 0 )
    {
        error = strerror( errno );
        throw Exception( "Error on semaphore wait: " + error );
    }
}

void Semaphore::v( int index )
{
    std::string error;
    struct sembuf operation;

    operation.sem_num = index; // numero de semaforo
    operation.sem_op = 1; // sumar 1 al semaforo
    operation.sem_flg = 0;

    if ( semop( m_id, &operation, 1 ) < 0 )
    {
        error = strerror( errno );
        throw Exception( "Error on semaphore signal: " + error );
    }
}

void Semaphore::wait( int index )
{
    this->p( index );
}

void Semaphore::signal( int index )
{
    this->v( index );
}

Semaphore Semaphore::create( std::string file, int charKey,
        int semAmmount, int flags )
{
    std::string error;
    std::string touchCmd = "touch " + file;

    system( touchCmd.c_str() );

    key_t key = ftok( file.c_str(), charKey );

    if ( key < 0 )
    {
        error = strerror( errno );
        throw Exception(
                "Error while creating the semaphore key on file: " + file
                        + ". Error: " + error );
    }

    if ( access( file.c_str(), R_OK ) != 0 )
    {
        error = strerror( errno );
        throw Exception(
                "No permissions for file: " + file + ". Error: " + error );
    }

    flags |= SEM_PERMS;
    int id = semget( key, semAmmount, flags );

    if ( id < 0 )
    {
        error = strerror( errno );
        throw Exception( "Error while getting the semaphore: " + error );
    }

    return Semaphore( id, semAmmount );
}

Semaphore Semaphore::get( std::string file, int charKey, int semAmmount )
{
    return Semaphore::create( file, charKey, semAmmount, IPC_EXCL );
}

void Semaphore::destroy( std::string file, int charKey )
{
    std::string error;

    Semaphore sem = Semaphore::get( file, charKey );

    if ( semctl( sem.m_id, 0, IPC_RMID ) < 0 )
    {
        error = strerror( errno );
        throw Exception(
                "Error while deleting the semaphore. Error: " + error );
    }
}

} /* namespace ipc */
